package com.itcoon.cloud.framework.transform.assemble.assembler.inter;


import com.itcoon.cloud.framework.transform.definition.InjectDefinition;
import com.itcoon.cloud.framework.transform.definition.ModelDefinition;
import com.itcoon.cloud.framework.transform.exception.AssembleException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;

public class InterDefaultMethodAssembler implements MethodAssembler {

    private static final Logger log = LoggerFactory.getLogger(InterDefaultMethodAssembler.class);

    @Override
    public boolean support(Class<?> targetClass, Object source, Method method, String[] ignoreProperties) {
        boolean flag = targetClass.isInterface() && method.isDefault();
        ModelDefinition md = BEAN_HOLDER.getModelDefinition(targetClass, source);
        return flag && md.getPropertyDefinitions().stream().anyMatch(m->m.getTargetGetter().equals(method));
    }

    @Override
    public <T, R> Object assemble(T source, Class<R> targetClass, Object proxy, Method method, String[] ignoreProperties, List<InjectDefinition> injectInfos, Object[] args) throws Exception {
        Class<?> declaringClass = method.getDeclaringClass();
        Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
        constructor.setAccessible(true);
        try {
            return constructor
                    .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
                    .unreflectSpecial(method, declaringClass)
                    .bindTo(proxy)
                    .invokeWithArguments(args);
        } catch (Throwable throwable) {
            log.error(throwable.getMessage(), throwable);
            throw new AssembleException(throwable.getMessage());
        }
    }
}
